/********************************************************************************
 * Copyright(c) 2020-2025 BST All rights reserved.
 * - Filename
 * - Author
 * - Version V1.0.0
 * - Date    2024/08/28
 * - Brief
 * - FunctionList:
 ******************************************************************************
 * History:
 *
 *
 *
 ******************************************************************************
 */
#include "Recorder.h"

#include <google/protobuf/util/delimited_message_util.h>

#include <filesystem>
#include <memory>

#include "FileUtil.h"
#include "TimeUtil.h"

using namespace std;
using namespace lidar;
using namespace google::protobuf::util;

#define SPACE_LEFT_MIN ((uintmax_t)2147483648)  // 2G

static void convert(pcl::PointCloud<pcl::PointXYZI> &cloud,
                    const std::shared_ptr<lpc::LidarProtoPointCloud> &frame) {
  cloud.width = frame->width();
  cloud.height = frame->height();
  cloud.is_dense = frame->is_dense();
  cloud.points.resize(frame->width() * frame->height());
  for (int index = 0; index < frame->data_size() / 4; index++) {
    cloud.points[index].x = frame->data(4 * index + 0);
    cloud.points[index].y = frame->data(4 * index + 1);
    cloud.points[index].z = frame->data(4 * index + 2);
    cloud.points[index].intensity = frame->data(4 * index + 3);
  }
  
}

static bool noSpaceLeft(const std::string &root) {
  auto info = filesystem::space(root);
  if (info.free < SPACE_LEFT_MIN) {
    return true;
  } else {
    return false;
  }
}

LidarRecoder::LidarRecoder(std::vector<std::unique_ptr<Lidar>> &&lidars,
                           const std::string &path)
    : _savedPath(path),_lidars(std::move(lidars)) {}
LidarRecoder::~LidarRecoder() {}
void LidarRecoder::start() {
  for (auto &lidar : _lidars) {
    lidar->start([&](std::shared_ptr<lpc::LidarProtoPointCloud> &&frame) {
      auto m_frame = std::move(frame);
      if (_controlCmd == "StartRecord") {
        if (noSpaceLeft(_savedPath)) {
          std::cout << " no space left !!" << std::endl;
        } else {
          auto us = m_frame->timestamp();
          auto id = m_frame->lidar_id();
          auto s = (long)(us / 1000000);
          tm ts = *localtime(&s);
          auto dir = getDirectory(ts, id);
          DIR *dirFd{nullptr};
          dirFd = opendir(dir.c_str());
          if (dirFd == nullptr) {
            FileUtil::mkpath(dir.c_str(), 0755);
          } else {
            closedir(dirFd);
          }
          if (_isDbfile) {
            std::ofstream ostream{};
            auto filePath = getFilePath(us, dir, "db");
            ostream.open(filePath, ios::out | ios::trunc | ios::binary);
            SerializeDelimitedToOstream(*m_frame, &ostream);
            ostream.flush();
            ostream.close();
            std::cout << filePath << std::endl;
          } else {
            pcl::PointCloud<pcl::PointXYZI> cloud;
            convert(cloud, m_frame);
            auto filePath = getFilePath(us, dir, "pcd");
            pcl::io::savePCDFileBinary(filePath, cloud);
          }
        }
      }
    });
  }
}

void LidarRecoder::setDbFile(const bool &v) { _isDbfile = v; }

void LidarRecoder::stop() {
  for (auto &lidar : _lidars) {
    lidar->stop();
  }
  std::cout << "Recoder Exit..." << std::endl;
}

void LidarRecoder::control(const std::string &cmd) {
  _controlCmd = cmd;
  std::string sTimestamp;
  char acTimestamp[128];
  struct timeval tv;
  struct tm *tm;
  gettimeofday(&tv, NULL);
  tm = localtime(&tv.tv_sec);
  sprintf(acTimestamp, "%04d-%02d-%02d %02d:%02d:%02d.%03d", tm->tm_year + 1900,
          tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
          (int)(tv.tv_usec / 1000));

  sTimestamp = acTimestamp;
  std::cout << sTimestamp << ", ControlCmd: " << _controlCmd << " "
            << std::endl;
}

std::string LidarRecoder::getDirectory(const tm &ts, uint32_t id) {
  char time[80];
  char dir[160];
  std::string m_path{};
  strftime(time, sizeof(time), "%Y-%m-%d-%H", &ts);
  sprintf(dir, "%s/lidar_%d/", time, id);
  m_path.append(_savedPath);
  m_path.append(std::string(dir));
  return m_path;
}

std::string LidarRecoder::getFilePath(uint64_t us, const std::string &dir,
                                      const std::string &file) {
  std::string m_dir;
  m_dir.append(dir);
  m_dir.append(std::to_string(us));
  m_dir.append(".");
  m_dir.append(file);
  return m_dir;
}